package com.winning.config;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.boot.autoconfigure.amqp.RabbitRetryTemplateCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class MyRabbitMqConfig {
    public static final String BUSINESS_EXCHANGE_NAME="my.direct.exchange";
    public static final String BUSINESS_ROUTING_KEY="hello";


    @Bean(name="oneSyncConnectionFactory")
    @Primary
    public ConnectionFactory oneSyncConnectionFactory(
            @Value("${spring.rabbitmq.host}") String host,
            @Value("${spring.rabbitmq.port}") int port,
            @Value("${spring.rabbitmq.username}") String username,
            @Value("${spring.rabbitmq.password}") String password,
            @Value("${spring.rabbitmq.publisher-confirms}") boolean publichConfirms,
            @Value("${spring.rabbitmq.publisher-returns}") boolean publisherReturns){
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setPublisherConfirms(publichConfirms);
        connectionFactory.setPublisherReturns(publisherReturns);
        return connectionFactory;
    }


  /*  @Bean(name="twoSyncConnectionFactory")
    public ConnectionFactory hospSyncConnectionFactory(
            @Value("${spring.rabbitmq.another.host}") String host,
            @Value("${spring.rabbitmq.another.port}") int port,
            @Value("${spring.rabbitmq.another.username}") String username,
            @Value("${spring.rabbitmq.another.password}") String password){
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        return connectionFactory;
    }

    *//**
     *  加了序列化和函数回调的RabbitTemplate
     *  加了@ConditionalOnSingleCandidate这个注解会导致自己定义的配置类无法注入 no reason
     *  @ConditionalOnSingleCandidate(ConnectionFactory.class)
     *//*
    @Bean
    public RabbitTemplate twoRabbitTemplate(@Qualifier("twoSyncConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        *//* 使用json序列化方式替换jdk序列化 *//*
        MessageConverter messageConverter = new Jackson2JsonMessageConverter();
        rabbitTemplate.setMessageConverter(messageConverter);
        return rabbitTemplate;
    }*/

    /**
     *  加了序列化和函数回调的RabbitTemplate
     *  加了@ConditionalOnSingleCandidate这个注解会导致自己定义的配置类无法注入 no reason
     *  @ConditionalOnSingleCandidate(ConnectionFactory.class)
     */
    @Bean
    //@Primary oneRabbitTemplate是RabbitTemplate的第一候选
    public RabbitTemplate oneRabbitTemplate(@Qualifier("oneSyncConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        /* 使用json序列化方式替换jdk序列化 */
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
        rabbitTemplate.setBeforePublishPostProcessors(new CorrelationMessagePostProcessor()); //可以在消费者直接得到correlationId
        /*
         * 若使用confirm-callback或return-callback，
         * 必须要配置publisherConfirms或publisherReturns为true
         * 每个rabbitTemplate只能有一个confirm-callback和return-callback
         *
         */
         rabbitTemplate.setConfirmCallback(createExchangeCheckCallback());

        /*
         * 使用return-callback时必须设置mandatory为true，
         * 或者在配置中设置mandatory-expression的值为true，
         * 可针对每次请求的消息去确定'mandatory'的boolean值，
         * 只能在提供'return-callback'时使用,和事务互斥
         */
        rabbitTemplate.setReturnCallback(createQueueCheckCallback());
        rabbitTemplate.setMandatory(true);
        return rabbitTemplate;
    }




    /**
     * 关于 ExchangeCheckCallback 和 QueueCheckCallback 的回调说明：
     * 1.如果消息没有到exchange,则confirm回调,ack=false
     * 2.如果消息到达exchange,则confirm回调,ack=true
     * 3.exchange到queue成功,则不回调QueueCheckCallback,只会回调ExchangeCheckCallback
     * 4.exchange到queue失败,则回调QueueCheckCallback(需设置mandatory=true,否则不回调,消息就丢了)
     */

    @Bean
    public ExchangeCheckCallback createExchangeCheckCallback(){
        return new ExchangeCheckCallback();
    }

    @Bean
    public QueueCheckCallback createQueueCheckCallback(){
        return new QueueCheckCallback();
    }



    /******************************************橙联方式*********************************************/
    /**
     * commonBusinessExchange=>deductQueue=>deductBusinessDeadQueue
     *
     * 通用业务交换机
     * 我发现以下创建元数据(交换机,队列,绑定关系)的代码只有在代码中调用RabbitTemplate后
     * 才会去RabbitMq中创建元数据,项目启动时是不会去创建的!!!
     */
    @Bean
    public DirectExchange commonBusinessExchange() {
        return new DirectExchange("commonBusinessExchange", true, false);
    }

    /**
     * 创建扣费业务队列
     *
     * RabbitMQ默认有一个exchange，叫default exchange，它用一个空字符串表示!!!，它是direct exchange类型，
     * "x-dead-letter-exchange", ""
     * 任何发往这个exchange的消息都会被路由到routing key的名字对应的队列上，如果没有对应的队列，则消息会被丢弃
     */
    @Bean
    public Queue deductQueue() {
        return QueueBuilder.durable("deductBusinessQueue")  //队列名字
                .withArgument("x-dead-letter-exchange", "") //发送交换机名字
                .withArgument("x-dead-letter-routing-key", "deductBusinessDeadQueue") //发送交换机路由key
                .build();
    }

    /**
     * 创建公用业务Exchange与Queue的绑定关系
     */
    @Bean
    public Binding deductBinding() {
        return BindingBuilder.bind(deductQueue()).
                to(commonBusinessExchange()).
                with("deductBusinessRoutingKey");
    }

    /**
     * 扣费失败的死信队列
     * 这里 x-dead-letter-routing-key 并没有指定那就用原本的routing-key即
     */
    @Bean
    public Queue deductDeadLetterQueue() {
        return QueueBuilder.durable("deductBusinessDeadQueue").build();
    }

}
